/*
 * direccionamiento.c
 *
 *  Created on: 14/10/2011
 *      Author: utn_so
 */


/* retorna puntero a array con los sectores que conforma el cluster número n */

//#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "colas.h"
#include "direccionamiento.h"
#include "iniciarFat.h" //OJO
#include "estaticas.h"
#include "colas.h"
#include "cache.h"
#include "estructuras_generales.h"
#include "funciones_NIPC.h"


uint32_t *DeClusterASectores(uint32_t n)
{
	uint32_t *sectores;
	sectores = (uint32_t *) malloc (bootSector->sectoresPorCluster * sizeof(uint32_t));
	char j;
	for (j=0; j < bootSector->sectoresPorCluster; j++)
		*(sectores + j) = 1 + j + bootSector->sectorComienzoFat + (bootSector->sectoresPorFat * bootSector->cantidadDeFat) + (n * bootSector->sectoresPorCluster);
	return sectores;
}

uint32_t *DeClusterABloques (uint32_t numeroCluster)
{
	uint32_t *bloques;
	bloques = (uint32_t *) malloc (sizeof(uint32_t) * BLOQUES_POR_CLUSTER);

	uint32_t inicioDataRegion = bootSector->sectorComienzoFat + (bootSector->sectoresPorFat * bootSector->cantidadDeFat);
	uint32_t i;

	for (i = 0; i < BLOQUES_POR_CLUSTER; i++)
		*(bloques + i) = (((inicioDataRegion + (numeroCluster - bootSector->clusterRaiz) * 8)) / SECTORES_POR_BLOQUE) + i; //fixme corregido: PROBAR DE NUEVO

	return bloques;
}

uint32_t *DeBloqueASectores(uint32_t numeroBloque)
{
	uint32_t *sectores;
	sectores = (uint32_t *) malloc (SECTORES_POR_BLOQUE * sizeof(uint32_t)); /* 8 es cantidad de sectores por bloque */
	char j;
	for (j=0; j < SECTORES_POR_BLOQUE; j++)	/* 8 es cantidad de sectores por bloque */
		*(sectores + j) = (numeroBloque * SECTORES_POR_BLOQUE) + j ;

	return sectores;
}

/* pensada para bloque de 4k
struct colaBloques *armarBloqueCluster(uint32_t iClus)
{
	uint32_t *sectores;
	sectores = (uint32_t *) malloc ( SECTORES_POR_BLOQUE * sizeof(uint32_t)); // 8 es sectores por bloque

	struct colaBloques *cola;
	CrearColaBloque(cola);

	sectores = DeClusterASectores(iClus);

	uint32_t i;

	for (i = 0; i < SECTORES_POR_BLOQUE ; i++)
		MeterEnColaBloque(cola, *(sectores + i)); // si el bloque al que pertenece el sector ya está en cola, sólo suma el contador "cantidad"

	return cola;
}

*/

char *SolicitarCluster(uint32_t iClus, char *path)
{
	char *cluster, *bloque;
	cluster = (char *) malloc (TAMANO_CLUSTER);
//	bloque = (char *) malloc (TAMANO_BLOQUE);

	uint32_t *bloques, i;
//	bloques = (uint32_t *) malloc (sizeof(uint32_t) * BLOQUES_POR_CLUSTER);

	/* bloques: vector de números de bloque que conforman el cluster */
	bloques = DeClusterABloques(iClus);



	for (i = 0; i < BLOQUES_POR_CLUSTER; i++)
	{
		bloque = SolicitarBloque(*(bloques + i), path);
		memcpy(cluster + (i * TAMANO_BLOQUE), bloque, TAMANO_BLOQUE);
		free(bloque);
	}

	free(bloques);
	return cluster;
}
char * recibirSectores(uint32_t descriptor){


	if(!descriptor){

		return NULL;
	}

	struct nipc_protocol  toReciveFst;
	struct nipc_protocol  toReciveSnd;
	char * bloque = calloc(1, TAMANO_BLOQUE);


		NIPC_recibir(&descriptor, &toReciveFst);
		NIPC_recibir(&descriptor, &toReciveSnd);


		if(toReciveFst.numeroSector > toReciveSnd.numeroSector){

			memcpy(bloque, toReciveSnd.payload, TAMANO_SECTOR);
			memcpy(bloque+512, toReciveFst.payload, TAMANO_SECTOR);
		}
		else{
			memcpy(bloque, toReciveFst.payload, TAMANO_SECTOR);
			memcpy(bloque+512, toReciveSnd.payload, TAMANO_SECTOR);

		}
	return bloque;


}

char *solicitarSector (uint32_t numeroSector, uint32_t descriptor)
{


	/*
	 * Pasos
	 * 1) Buscar una conexión libre
	 * 2) Armar una estructura NIPC para realizar el pedido
	 * 3) Hacer el pedido
	 * 4) Esperar el retorno
	 * 5) Liberar el descriptor
	 * 6) Devolver sólo el valor del payload
	 */

	if(!descriptor){

		return NULL;
	}

	struct nipc_protocol  toRecive;

		NIPC_enviar(descriptor,'2',512,"",numeroSector);


	return &(toRecive.payload[0]);

/*
	char *sector;
	sector = (char *) malloc (TAMANO_SECTOR);

// solicitar sector para pruebas 	 fixme PRUEBAS
	FILE *fat32;
	fat32 = fopen("/home/utn_so/Escritorio/Discos/small_fat32.disk","r+");
//	fat32 = fopen("/home/utn_so/Desarrollo/big_fat32.disk","r+");
	uint32_t a;
	uint32_t desplazamiento = numeroSector * TAMANO_SECTOR;
	a = fseek(fat32, desplazamiento, SEEK_SET);
	a = fread (sector, 1, TAMANO_SECTOR, fat32);
	fclose (fat32);
//FIN solicitar sector para pruebas		fixme PRUEBAS



	// hace el pedido del sector a otro proceso

	return sector;
*/
}

char *SolicitarBloque(uint32_t numeroBloque, char *path) //bloque es número de bloque, devuelve puntero al bloque
{
	uint32_t *sectores;
//	sectores = (uint32_t *) malloc (SECTORES_POR_BLOQUE * sizeof(uint32_t)); /* 8 es la cantidad de sectores por bloque */
	struct colaDeCache *cache;
	struct nodoDeCache *nodo, *nodoParaSustituir;

	sectores = DeBloqueASectores(numeroBloque);
	uint32_t i;

	char *bloque, *sector;

//	sector = (char *) malloc (TAMANO_SECTOR); //bytes por sector. No uso bootSector->.. porque cuando pido el primer sector no coozco este dato

	if (path != 0 && config.iTamCache != 0)
	{
		// fixme BUSCAR EN CACHE y retornar el bloque


		cache = buscarCacheDeUnArchivo(path);

		pthread_mutex_lock(&(cache->mutexCacheArchivo));
		nodo = buscarEnCache(numeroBloque, cache);


		if (nodo != NULL)
		{
			free(sectores);
			pthread_mutex_unlock(&(cache->mutexCacheArchivo));

			bloque = (char *) malloc (TAMANO_BLOQUE);
			memcpy(bloque, nodo->contenido, TAMANO_BLOQUE);

//			FILE *cache;
//			cache = fopen("/home/utn_so/Escritorio/informe.txt","a+");
//			if (numeroBloque >= 1044)
//				fprintf(cache,"se leyó el bloque %d \n ", numeroBloque);
//			fclose(cache);

			return (bloque);


		}

	}

	// if path == 0 o no lo encontré en cache


//	for (i = 0; i < SECTORES_POR_BLOQUE; i++) /* 8 es la cantidad de sectores por bloque */
//	{

//	pthread_mutex_lock(&mutexDireccionamiento);

	int32_t descriptor = obtenerConexion();

		solicitarSector (*(sectores + 0), descriptor);
		log_info(log, "DIRECC", "El descriptor %d mandó el sector %d",*(sectores + 0), descriptor );
		solicitarSector (*(sectores + 1), descriptor);
		log_info(log, "DIRECC", "El descriptor %d mandó el sector %d",*(sectores + 1), descriptor );

		bloque = recibirSectores(descriptor);

		liberarConexion(descriptor);
		//	pthread_mutex_unlock(&mutexDireccionamiento);
		/* hacer el pedido del sector sectores[i] y guardarlo en sector; */
//		memcpy (bloque + (i * TAMANO_SECTOR), sector, TAMANO_SECTOR);
		
//	}	//bytes por sector. No uso bootSector->.. porque cuando pido el primer sector no coozco este dato

	free (sectores);


	//fixme agregar bloque en la cache
	//		y copiar bloque sustituido en el disco

	if (path != 0 && config.iTamCache != 0)
	{
		nodo = (struct nodoDeCache *) malloc (sizeof(struct nodoDeCache));
//		nodoParaSustituir = (struct nodoDeCache *) malloc (sizeof(struct nodoDeCache));
		nodo->contenido = (char *) malloc (TAMANO_BLOQUE);

		memcpy(nodo->contenido, bloque, TAMANO_BLOQUE);
		nodo->numeroBloque = numeroBloque;
		nodo->siguiente = NULL;


		nodoParaSustituir = agregarALaCache(nodo, cache);


		if (nodoParaSustituir != NULL)
			enviarBloque(nodoParaSustituir->contenido, nodoParaSustituir->numeroBloque, 0);

		pthread_mutex_unlock(&(cache->mutexCacheArchivo));
//
		if (nodoParaSustituir != NULL)
		{
			free(nodoParaSustituir->contenido);
			free(nodoParaSustituir);
		}
//		free(nodo);
	}
	return bloque;


}


uint32_t enviarSector(char *sector, uint32_t numeroSector)
{

	/*
	 * Pasos
	 * 1) Buscar una conexión libre
	 * 2) Armar una estructura NIPC para intentar persistir el sector
	 * 3) Hacer el pedido de persistencia del sector
	 * 4) Esperar el retorno
	 * 5) Liberar el descriptor
	 * 6) Devolver 1 si el type es 3, 0 si es cualquier otra cosa
	 */


	int32_t descriptor =obtenerConexion();

		if(!descriptor){
			log_error(log, "DIRECCIONAMIENTO", "Error en el obtenerConexion del solicitarSector");

			return NULL;
		}



			NIPC_enviar(descriptor,'1',TAMANO_SECTOR,sector,numeroSector);

			liberarConexion(descriptor);

		return 1;


	/* enviar sector para pruebas  fixme PRUEBAS*/
/*
	FILE *fat32;
	fat32 = fopen("/home/utn_so/Escritorio/Discos/small_fat32.disk","r+");
//	fat32 = fopen("/home/utn_so/Desarrollo/big_fat32.disk","r+");

	uint32_t desplazamiento = numeroSector * 512;
	fseek(fat32, desplazamiento, SEEK_SET);
	fwrite(sector, 512, 1, fat32);
	fclose (fat32);

	/* FIN enviar sector para pruebas  fixme PRUEBAS*/

//	/*envia el sector al otro proceso */
//
//	return 1;

}




uint32_t enviarBloque (char *bloque, uint32_t numeroBloque, char *path)
{
	uint32_t *sectores;
//	sectores = (uint32_t *) malloc ( SECTORES_POR_BLOQUE * sizeof(uint32_t)); /* 8 es la cantidad de sectores por bloque */

	sectores = DeBloqueASectores(numeroBloque);

	char *sector;
	sector = (char *) malloc (bootSector->bytesPorSector);

	struct colaDeCache *cahe;
	struct nodoDeCache *nodo, *nodoAux;
	uint32_t i;

	if (path != 0 && config.iTamCache != 0)
	{
		// fixme escribir en la cache, sino está en cache
		// 		 agregarlo a la cache y enviar el bloque
		//		 sustituido a disco
		nodo = (struct nodoDeCache *) malloc (sizeof(struct nodoDeCache));
		nodo->contenido = (char *) malloc (TAMANO_BLOQUE);

		memcpy(nodo->contenido, bloque, TAMANO_BLOQUE);
		nodo->numeroBloque = numeroBloque;
		nodo->siguiente = NULL;

		struct colaDeCache *cache;


		cache = buscarCacheDeUnArchivo(path);


		pthread_mutex_lock(&(cache->mutexCacheArchivo));

		nodoAux = agregarALaCache(nodo, cache); //retorna el nodo sustituido o null


		if (nodoAux != NULL)
		{
			enviarBloque(nodoAux->contenido,nodoAux->numeroBloque, 0);
			free (nodoAux->contenido);
			free (nodoAux);
		}

		pthread_mutex_unlock(&(cache->mutexCacheArchivo));
	}
	else
	{
		for (i = 0; i < SECTORES_POR_BLOQUE; i++) /* 8 es la cantidad de sectores por bloque */
		{
			memcpy (sector, bloque + (bootSector->bytesPorSector * i), bootSector->bytesPorSector);
			enviarSector(sector, sectores[i]);
		}


	}
	free(sector);
	free(sectores);
	return 1;
}



uint32_t enviarCluster (uint32_t iClus, char *cluster, char *path)
{
	char *bloque;
	bloque = (char *) malloc (TAMANO_BLOQUE);

	uint32_t *bloques, i;
//	bloques = (uint32_t *) malloc (sizeof(uint32_t) * BLOQUES_POR_CLUSTER);
	bloques = DeClusterABloques(iClus);

	for (i = 0; i < BLOQUES_POR_CLUSTER; i++)
	{
		memcpy(bloque, cluster + (i * TAMANO_BLOQUE), TAMANO_BLOQUE);
		enviarBloque(bloque, *(bloques + i), path);
	}

	free(bloque);
	free(bloques);
	return 1;
}


void actualizarEnFat(struct colaBloquesFAT *colaBloques)
{
	uint32_t numeroBloque;
	char *bloque;

	while (colaBloques->frente != NULL)
	{
		pthread_mutex_lock(&mutexFat);
		numeroBloque = SacarDeColaBloqueFAT(colaBloques);
		bloque = (char *) &fat[(numeroBloque - 16) * 256];  //al puntero "bloque" le asigno la posición de memoria en la fat en donde empieza el bloque.
		pthread_mutex_unlock(&mutexFat);
		/* 16 es bloque inicio de FAT*/
		/* 256 cantidad de punteros de la fat en un bloque */
		enviarBloque(bloque, numeroBloque, 0);
	}
	return;
}

uint32_t bloqueDeFat(uint32_t ClusterDeFat) //devuelve el número de bloque en el que está el puntero al cluster ClusterDeFat
{
	return (ClusterDeFat / 256) + 16;
}
